#include "TwitterAPIWrap.h"



TwitterAPIWrap::TwitterAPIWrap(void)
{
}

TwitterAPIWrap::~TwitterAPIWrap(void)
{
}

class SSLInitializer
{
public:
	SSLInitializer()
	{
		Poco::Net::initializeSSL();
	}
	
	~SSLInitializer()
	{
		Poco::Net::uninitializeSSL();
	}
};


std::istream* TwitterAPIWrap::open(const URI& uri, std::string username, std::string password)
{
	SSLInitializer sslInitializer;

	// Note: we must create the passphrase handler prior Context 
	SharedPtr<InvalidCertificateHandler> ptrCert = new Poco::Net::AcceptCertificateHandler(false); // ask the user via console
	Context::Ptr ptrContext = new Context(Context::CLIENT_USE, "", "", "", Context::VERIFY_RELAXED, 9, true, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
	SSLManager::instance().initializeClient(0, ptrCert, ptrContext);

    unsigned long MAX_REDIRECTS = 10;
	Poco::Net::HTTPClientSession* pSession = 0;
	try
	{
		bool retry = false;
		int redirects = 0;
		do
		{
			if (uri.getScheme() != "http")
				pSession = new Poco::Net::HTTPSClientSession(uri.getHost(), uri.getPort());
			else
				pSession = new Poco::Net::HTTPClientSession(uri.getHost(), uri.getPort());

			std::string path = uri.getPathAndQuery();
			if (path.empty()) path = "/";
			Poco::Net::HTTPRequest req(Poco::Net::HTTPRequest::HTTP_GET, path, Poco::Net::HTTPMessage::HTTP_1_1);
			Poco::Net::HTTPBasicCredentials cred(username, password);
			cred.authenticate(req);
			pSession->sendRequest(req);
			Poco::Net::HTTPResponse res;
			std::istream& rs = pSession->receiveResponse(res);
			bool moved = (res.getStatus() == Poco::Net::HTTPResponse::HTTP_MOVED_PERMANENTLY || 
			              res.getStatus() == Poco::Net::HTTPResponse::HTTP_FOUND || 
			              res.getStatus() == Poco::Net::HTTPResponse::HTTP_SEE_OTHER ||
						  res.getStatus() == Poco::Net::HTTPResponse::HTTP_TEMPORARY_REDIRECT);
			if (moved)
			{
				delete pSession; pSession = 0;
				++redirects;
				retry = true;
			}
			else if (res.getStatus() == Poco::Net::HTTPResponse::HTTP_OK)
			{
				return new Poco::Net::HTTPResponseStream(rs, pSession);
			}
			else if (res.getStatus() == Poco::Net::HTTPResponse::HTTP_USEPROXY && !retry)
			{
				// The requested resource MUST be accessed through the proxy 
				// given by the Location field. The Location field gives the 
				// URI of the proxy. The recipient is expected to repeat this 
				// single request via the proxy. 305 responses MUST only be generated by origin servers.
				// only use for one single request!

				delete pSession; pSession = 0;
				retry = true; // only allow useproxy once
			}
			else 
			{
				delete pSession; pSession = 0;
				throw Poco::Net::HTTPException(res.getReason(), uri.toString());
			}
		}
		while (retry && redirects < MAX_REDIRECTS);
		throw Poco::Net::HTTPException("Too many redirects", uri.toString());
	}
	catch (...)
	{
		delete pSession;
		throw;
	}
}

std::istream * TwitterAPIWrap::doSearchAPI(TwitterQuery * query)
{
	try
	{
		URI uri1("http://search.twitter.com/search.json?" + query->getQuery());

		return open(uri1, mUserID, mPassWord);
	}
	catch (Exception& exc)
	{
		std::cerr << exc.displayText() << std::endl;
		return 0;
	}
}

std::istream * TwitterAPIWrap::doStreamTrackAPI(TwitterQuery * query)
{
	SSLInitializer sslInitializer;

	try
	{
		URI uri("https://stream.twitter.com/1/statuses/filter.json?" + query->getQuery());

		return open(uri, mUserID, mPassWord);
	}
	catch (Exception& exc)
	{
		std::cerr << exc.displayText() << std::endl;
		return 0;
	}
}

std::istream * TwitterAPIWrap::doStreamSample(TwitterQuery * query)
{
	SSLInitializer sslInitializer;

	try
	{
		URI uri("https://stream.twitter.com/1/statuses/sample.json?" + query->getQuery());

		return open(uri, mUserID, mPassWord);
	}
	catch (Exception& exc)
	{
		std::cerr << exc.displayText() << std::endl;
		return 0;
	}
}

string TwitterAPIWrap::getOauthContent(string url, TwitterFriendFlowerQuery *query)
{
	OAuthBaseInfo oauthInfo;
	query->getQueryOauth(oauthInfo);

	//step 1: base string
	string basestring = oauthInfo.getBaseString();
	string enUrl;
	URI::encode(url, ":/+&= ", enUrl);
	basestring = "GET&" + enUrl + '&' + basestring;

	//step 2: Get signature
	Poco::HMACEngine<Poco::SHA1Engine> hmac(oauthInfo.getContmSecret() + '&' + oauthInfo.getTokenSecret());
	hmac.update(basestring);

	const Poco::DigestEngine::Digest &dig = hmac.digest();

	stringstream ssbs;
	Poco::Base64Encoder bs(ssbs);
	Poco::DigestEngine::Digest::const_iterator pIr;
	for(pIr = dig.begin(); pIr != dig.end(); ++pIr){
		bs << *pIr;
	}
	bs.close();
	string sig;
	ssbs >> sig;
	string sig64;
	URI::encode(sig, ":/+&= ", sig64);


	//step 3: Make the delicious API call
	string content = "OAuth "; //realm=\"https://api.twitter.com\",
	content += oauthInfo.getContent();
	content += "oauth_signature=\"" + sig64 + '\"';

	return content;
}

istream* TwitterAPIWrap::doFriendsIds(TwitterFriendFlowerQuery *query)
{
	string url("https://api.twitter.com/1/friends/ids.json");
	string content = getOauthContent(url, query);

	try{
		URI uri(url + '?' + query->getQuery());

		return open(uri, content);
	}

	catch (Exception& exc)
	{
		std::cerr << exc.displayText() << std::endl;
		return 0;
	}
 
}

std::istream* TwitterAPIWrap::open(const URI& uri, std::string content)
{
	SSLInitializer sslInitializer;

	// Note: we must create the passphrase handler prior Context 
	SharedPtr<InvalidCertificateHandler> ptrCert = new Poco::Net::AcceptCertificateHandler(false); // ask the user via console
	Context::Ptr ptrContext = new Context(Context::CLIENT_USE, "", "", "", Context::VERIFY_RELAXED, 9, true, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
	SSLManager::instance().initializeClient(0, ptrCert, ptrContext);
   
	unsigned long MAX_REDIRECTS = 10;
	Poco::Net::HTTPClientSession* pSession = 0;
	try
	{
		bool retry = false;
		int redirects = 0;
		do
		{
			if (uri.getScheme() != "http")
				pSession = new Poco::Net::HTTPSClientSession(uri.getHost(), uri.getPort());
			else
				pSession = new Poco::Net::HTTPClientSession(uri.getHost(), uri.getPort());

			std::string path = uri.getPathAndQuery();
			if (path.empty()) path = "/";
			Poco::Net::HTTPRequest req(Poco::Net::HTTPRequest::HTTP_GET, path, Poco::Net::HTTPMessage::HTTP_1_1);

			req.add("Authorization",content); 
			req.setContentType("application/x-www-form-urlencoded");

			(pSession->sendRequest(req));
			
			Poco::Net::HTTPResponse res;
			std::istream& rs = pSession->receiveResponse(res);
			bool moved = (res.getStatus() == Poco::Net::HTTPResponse::HTTP_MOVED_PERMANENTLY || 
			              res.getStatus() == Poco::Net::HTTPResponse::HTTP_FOUND || 
			              res.getStatus() == Poco::Net::HTTPResponse::HTTP_SEE_OTHER ||
						  res.getStatus() == Poco::Net::HTTPResponse::HTTP_TEMPORARY_REDIRECT);

			if (moved)
			{
				delete pSession; pSession = 0;
				++redirects;
				retry = true;
			}
			else if (res.getStatus() == Poco::Net::HTTPResponse::HTTP_OK)
			{
				return new Poco::Net::HTTPResponseStream(rs, pSession);
			}
			else if (res.getStatus() == Poco::Net::HTTPResponse::HTTP_USEPROXY && !retry)
			{
				// The requested resource MUST be accessed through the proxy 
				// given by the Location field. The Location field gives the 
				// URI of the proxy. The recipient is expected to repeat this 
				// single request via the proxy. 305 responses MUST only be generated by origin servers.
				// only use for one single request!

				delete pSession; pSession = 0;
				retry = true; // only allow useproxy once
			}
			else 
			{
				delete pSession; pSession = 0;
				throw Poco::Net::HTTPException(res.getReason(), uri.toString());
			}
		}
		while (retry && redirects < MAX_REDIRECTS);
		throw Poco::Net::HTTPException("Too many redirects", uri.toString());
	}
	catch (...)
	{
		delete pSession;
		throw;
	}
}